#!/usr/bin/env python3
"""
Linux Task Manager
一个类似Windows任务管理器的Linux进程管理工具
"""

import sys
import os
import subprocess
import shutil
import stat
import psutil
import time
import multiprocessing
import logging
# 禁用Qt的一些警告信息
os.environ['QT_LOGGING_RULES'] = 'qt.qpa.xcb.warning=false'
os.environ['QT_X11_NO_MITSHM'] = '1'

# 设置日志级别来过滤掉一些Qt警告
logging.basicConfig(level=logging.CRITICAL)

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QTabWidget, QPushButton, QLabel, QGroupBox, QCheckBox,
    QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QMessageBox,
    QFileDialog, QComboBox, QProgressBar, QTableWidget, QTableWidgetItem,
    QAbstractItemView, QHeaderView, QInputDialog, QMenu, QAction, QDialog
)
from PyQt5.QtCore import Qt, QProcess, QTimer, pyqtSignal, QObject
from PyQt5.QtGui import QFont, QIcon, QColor, QPixmap, QPainter

import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


class NumericTableWidgetItem(QTableWidgetItem):
    """支持数值排序的表格项"""
    
    def __init__(self, value):
        super().__init__()
        self.value = value
        if isinstance(value, (int, float)):
            self.setText(str(value))
        else:
            self.setText(value)
            
    def __lt__(self, other):
        # 尝试将文本转换为数值进行比较
        try:
            # 如果是百分比格式，提取数值部分
            self_text = self.text().rstrip('%')
            other_text = other.text().rstrip('%')
            
            # 尝试转换为浮点数
            self_num = float(self_text) if self_text else 0
            other_num = float(other_text) if other_text else 0
            
            return self_num < other_num
        except ValueError:
            # 如果转换失败，使用默认的字符串比较
            return self.text() < other.text()


def get_processes_data(filter_type="all"):
    """在独立进程中获取进程数据"""
    try:
        processes = []
        # 只获取需要显示的字段，减少数据传输
        attrs = ['pid', 'name', 'status', 'username', 'cpu_percent', 
                'memory_percent', 'memory_info', 'create_time', 'cmdline', 'nice']
        
        # 系统进程判断集合
        system_users = {'root', 'daemon', 'bin', 'sys', 'sync', 'games', 'man', 
                       'lp', 'mail', 'news', 'uucp', 'proxy', 'www-data', 
                       'backup', 'list', 'irc', 'gnats', 'nobody', 'systemd'}
        
        system_processes = {
            'systemd', 'kthreadd', 'ksoftirqd', 'kworker', 'rcu_gp', 
            'rcu_par_gp', 'migration', 'idle', 'oom_reaper', 'writeback',
            'crypto', 'kcompactd', 'watchdog', 'khungtaskd', 'selinux',
            'kmemstick', 'deferwq', 'kblockd', 'ata', 'scsi', 'usb', 
            'bioset', 'xfs', 'jbd2', 'ext4', 'rpc', 'avahi', 'cups',
            'bluetooth', 'cron', 'atd', 'rsyslogd', 'acpid', 'network',
            'ModemManager', 'NetworkManager', 'lightdm', 'gdm', 'sddm'
        }
        
        def is_system_process(proc_info):
            """判断是否为系统进程"""
            username = proc_info.get('username', '')
            name = proc_info.get('name', '')
            pid = proc_info.get('pid', 0)
            
            # PID为0或1的通常是系统进程
            if pid <= 1:
                return True
            
            # 如果用户名是系统用户，或者是系统进程名，则认为是系统进程
            if username in system_users:
                return True
                
            for sys_proc in system_processes:
                if sys_proc in name:
                    return True
                    
            return False
            
        def is_service_process(proc_info):
            """判断是否为服务进程"""
            name = proc_info.get('name', '')
            cmdline = proc_info.get('cmdline', [])
            
            if cmdline:
                cmdline_str = ' '.join(cmdline)
                # 服务进程特征
                service_indicators = {
                    '/usr/sbin/', '/lib/systemd/', 'systemd-', '/bin/', 
                    'crond', 'atd', 'rsyslogd', 'cupsd', 'bluetoothd',
                    'avahi-daemon', 'dbus-daemon', 'NetworkManager', 
                    'ModemManager', 'lightdm', 'gdm', 'sddm'
                }
                
                # 检查命令行是否包含服务特征
                for indicator in service_indicators:
                    if indicator in cmdline_str or indicator in name:
                        return True
                        
                # 检查是否在/sbin/或/usr/bin/等系统目录下
                system_paths = ('/sbin/', '/usr/sbin/', '/lib/', '/usr/lib/')
                if cmdline_str.startswith(system_paths):
                    return True
            
            return False
            
        def is_user_process(proc_info):
            """判断是否为用户进程"""
            # 不是系统进程也不是服务进程的就是用户进程
            return not is_system_process(proc_info) and not is_service_process(proc_info)
        
        for proc in psutil.process_iter(attrs, ad_value=None):
            try:
                proc_info = proc.info
                # 根据当前过滤器类型决定是否添加进程
                if filter_type == "all":
                    processes.append(proc_info)
                elif filter_type == "user" and is_user_process(proc_info):
                    processes.append(proc_info)
                elif filter_type == "system" and is_system_process(proc_info):
                    processes.append(proc_info)
                elif filter_type == "services" and is_service_process(proc_info):
                    processes.append(proc_info)
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                pass
        
        # 按CPU使用率排序
        processes.sort(key=lambda x: x['cpu_percent'] or 0, reverse=True)
        
        # 处理数据，确保可以序列化
        result = []
        for proc in processes:
            processed_proc = {}
            for key, value in proc.items():
                if key == 'cmdline' and value:
                    processed_proc[key] = value[:10]  # 限制命令行参数数量
                else:
                    processed_proc[key] = value
            result.append(processed_proc)
            
        return result
    except Exception as e:
        return {"error": str(e)}


class LinuxTaskManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.process_pool = None
        # 保存排序状态
        self.sort_column = 4  # 默认按CPU%排序
        self.sort_order = Qt.DescendingOrder  # 默认降序（感官上箭头向下表示降序）
        
        # 设置应用图标 - 使用多种方法确保图标显示
        self.set_application_icon()
            
        self.init_ui()
        self.setup_timers()
        self.show_loading_message()
        self.load_initial_processes()
        self.update_system_info()
        # 连接表格头部点击事件
        self.process_table.horizontalHeader().sectionClicked.connect(self.on_header_clicked)
        
    def set_application_icon(self):
        """设置应用程序图标 - 使用安全的方法确保图标显示"""
        # 获取图标的绝对路径
        icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
        print(f"尝试加载图标: {icon_path}")
        
        if os.path.exists(icon_path):
            try:
                # 只使用QIcon，避免在QApplication创建前使用QPixmap
                icon = QIcon(icon_path)
                if not icon.isNull():
                    self.setWindowIcon(icon)
                    print("成功设置窗口图标")
                else:
                    print("图标加载失败，图标为空")
                    
                # 尝试使用绝对路径
                abs_path = os.path.abspath(icon_path)
                if icon_path != abs_path:
                    icon = QIcon(abs_path)
                    if not icon.isNull():
                        self.setWindowIcon(icon)
                        print(f"使用绝对路径成功设置窗口图标: {abs_path}")
            except Exception as e:
                print(f"设置图标时出错: {e}")
        else:
            print(f"图标文件不存在: {icon_path}")
        
    def init_ui(self):
        """初始化用户界面"""
        self.setWindowTitle("任务管理器")
        self.setGeometry(100, 100, 1000, 700)
        
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 创建标签页
        tab_widget = QTabWidget()
        main_layout.addWidget(tab_widget)
        
        # 创建各个功能标签页
        self.process_tab = self.create_process_tab()
        self.performance_tab = self.create_performance_tab()
        self.app_history_tab = self.create_app_history_tab()
        self.startup_tab = self.create_startup_tab()
        self.users_tab = self.create_users_tab()
        self.services_tab = self.create_services_tab()
        
        tab_widget.addTab(self.process_tab, "进程")
        tab_widget.addTab(self.performance_tab, "性能")
        tab_widget.addTab(self.app_history_tab, "应用历史记录")
        tab_widget.addTab(self.startup_tab, "启动")
        tab_widget.addTab(self.users_tab, "用户")
        tab_widget.addTab(self.services_tab, "服务")
        
        # 底部按钮
        button_layout = QHBoxLayout()
        
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.refresh_all)
        button_layout.addWidget(self.refresh_btn)
        
        button_layout.addStretch()
        
        self.end_task_btn = QPushButton("结束任务")
        self.end_task_btn.clicked.connect(self.end_task)
        button_layout.addWidget(self.end_task_btn)
        
        self.new_task_btn = QPushButton("新任务")
        self.new_task_btn.clicked.connect(self.new_task)
        button_layout.addWidget(self.new_task_btn)
        
        main_layout.addLayout(button_layout)
        
    def show_loading_message(self):
        """显示加载中提示"""
        self.process_table.setRowCount(1)
        loading_item = QTableWidgetItem("正在加载进程信息，请稍候...")
        loading_item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
        self.process_table.setItem(0, 0, loading_item)
        # 合并所有列
        self.process_table.setSpan(0, 0, 1, self.process_table.columnCount())
        
    def load_initial_processes(self):
        """加载初始进程数据"""
        # 在独立进程中获取进程数据
        self.process_pool = multiprocessing.Pool(processes=1)
        self.process_pool.apply_async(get_processes_data, args=("all",), callback=self.initial_processes_loaded)
        
    def initial_processes_loaded(self, processes):
        """初始进程数据加载完成"""
        # 在主线程中更新界面
        if isinstance(processes, dict) and "error" in processes:
            print(f"加载进程时出错: {processes['error']}")
            return
            
        self.current_filter = "all"
        self.update_process_table(processes, None, 0)
        
    def create_process_tab(self):
        """创建进程标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # 进程分类按钮il1Io0O
        process_filter_layout = QHBoxLayout()
        self.show_all_processes_btn = QPushButton("所有进程")
        self.show_all_processes_btn.clicked.connect(lambda: self.filter_processes("all"))
        self.show_all_processes_btn.setCheckable(True)
        self.show_all_processes_btn.setChecked(True)
        
        self.show_user_processes_btn = QPushButton("用户进程")
        self.show_user_processes_btn.clicked.connect(lambda: self.filter_processes("user"))
        self.show_user_processes_btn.setCheckable(True)
        
        self.show_system_processes_btn = QPushButton("系统进程")
        self.show_system_processes_btn.clicked.connect(lambda: self.filter_processes("system"))
        self.show_system_processes_btn.setCheckable(True)
        
        self.show_services_btn = QPushButton("后台服务")
        self.show_services_btn.clicked.connect(lambda: self.filter_processes("services"))
        self.show_services_btn.setCheckable(True)
        
        # 创建按钮组效果
        self.process_filter_buttons = [
            self.show_all_processes_btn,
            self.show_user_processes_btn,
            self.show_system_processes_btn,
            self.show_services_btn
        ]
        
        process_filter_layout.addWidget(self.show_all_processes_btn)
        process_filter_layout.addWidget(self.show_user_processes_btn)
        process_filter_layout.addWidget(self.show_system_processes_btn)
        process_filter_layout.addWidget(self.show_services_btn)
        process_filter_layout.addStretch()
        layout.addLayout(process_filter_layout)
        
        # 进程列表
        self.process_table = QTableWidget()
        self.process_table.setColumnCount(10)
        self.process_table.setHorizontalHeaderLabels([
            "进程名称", "PID", "状态", "用户", "CPU%", "内存%", 
            "内存", "运行时间", "命令行", "优先级"
        ])
        self.process_table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.process_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 设置右键菜单
        self.process_table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.process_table.customContextMenuRequested.connect(self.show_process_context_menu)
        # 设置列的排序方式
        self.process_table.horizontalHeader().setSortIndicatorShown(True)
        # 启用排序功能
        self.process_table.setSortingEnabled(True)
        # 设置列宽
        self.process_table.setColumnWidth(0, 150)  # 进程名称
        self.process_table.setColumnWidth(1, 80)   # PID
        self.process_table.setColumnWidth(2, 60)   # 状态
        self.process_table.setColumnWidth(3, 80)   # 用户
        self.process_table.setColumnWidth(4, 60)   # CPU%
        self.process_table.setColumnWidth(5, 60)   # 内存%
        self.process_table.setColumnWidth(6, 80)   # 内存
        self.process_table.setColumnWidth(7, 80)   # 运行时间
        self.process_table.setColumnWidth(8, 200)  # 命令行
        self.process_table.setColumnWidth(9, 60)   # 优先级
        layout.addWidget(self.process_table)
        
        # 初始化进程过滤器
        self.current_filter = "all"
        
        return tab
        
    def create_performance_tab(self):
        """创建性能标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # 初始化数据存储列表，用于绘制折线图
        self.cpu_data = []
        self.memory_data = []
        self.disk_data = []
        self.max_data_points = 50  # 最大显示数据点数
        
        # CPU使用率图表
        cpu_group = QGroupBox("CPU使用率")
        cpu_layout = QVBoxLayout(cpu_group)
        
        # 创建CPU图表
        self.cpu_figure = Figure(figsize=(5, 2), dpi=100)
        self.cpu_canvas = FigureCanvas(self.cpu_figure)
        self.cpu_ax = self.cpu_figure.add_subplot(111)
        self.cpu_ax.set_title("CPU使用率实时图表")
        self.cpu_ax.set_xlabel("时间点")
        self.cpu_ax.set_ylabel("使用率 (%)")
        self.cpu_ax.set_ylim(0, 100)
        cpu_layout.addWidget(self.cpu_canvas)
        
        self.cpu_label = QLabel("CPU: 0%")
        cpu_layout.addWidget(self.cpu_label)
        layout.addWidget(cpu_group)
        
        # 内存使用率图表
        memory_group = QGroupBox("内存")
        memory_layout = QVBoxLayout(memory_group)
        
        # 创建内存图表
        self.memory_figure = Figure(figsize=(5, 2), dpi=100)
        self.memory_canvas = FigureCanvas(self.memory_figure)
        self.memory_ax = self.memory_figure.add_subplot(111)
        self.memory_ax.set_title("内存使用率实时图表")
        self.memory_ax.set_xlabel("时间点")
        self.memory_ax.set_ylabel("使用率 (%)")
        self.memory_ax.set_ylim(0, 100)
        memory_layout.addWidget(self.memory_canvas)
        
        self.memory_label = QLabel("内存: 0% (0 MB / 0 MB)")
        memory_layout.addWidget(self.memory_label)
        layout.addWidget(memory_group)
        
        # 磁盘使用率图表
        disk_group = QGroupBox("磁盘")
        disk_layout = QVBoxLayout(disk_group)
        
        # 创建磁盘图表
        self.disk_figure = Figure(figsize=(5, 2), dpi=100)
        self.disk_canvas = FigureCanvas(self.disk_figure)
        self.disk_ax = self.disk_figure.add_subplot(111)
        self.disk_ax.set_title("磁盘使用率实时图表")
        self.disk_ax.set_xlabel("时间点")
        self.disk_ax.set_ylabel("使用率 (%)")
        self.disk_ax.set_ylim(0, 100)
        disk_layout.addWidget(self.disk_canvas)
        
        self.disk_label = QLabel("磁盘: 0% (0 MB / 0 MB)")
        disk_layout.addWidget(self.disk_label)
        layout.addWidget(disk_group)
        
        layout.addStretch()
        return tab
        
    def create_app_history_tab(self):
        """创建应用历史标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        label = QLabel("应用历史记录功能将在后续版本中实现")
        layout.addWidget(label)
        layout.addStretch()
        
        return tab
        
    def create_startup_tab(self):
        """创建启动标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        label = QLabel("启动项管理功能将在后续版本中实现")
        layout.addWidget(label)
        layout.addStretch()
        
        return tab
        
    def create_users_tab(self):
        """创建用户标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        label = QLabel("用户管理功能将在后续版本中实现")
        layout.addWidget(label)
        layout.addStretch()
        
        return tab
        
    def create_services_tab(self):
        """创建服务标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        label = QLabel("服务管理功能将在后续版本中实现")
        layout.addWidget(label)
        layout.addStretch()
        
        return tab
        
    def setup_timers(self):
        """设置定时器"""
        self.process_timer = QTimer()
        self.process_timer.timeout.connect(self.update_processes)
        self.process_timer.start(3000)  # 每3秒更新一次
        
        self.system_timer = QTimer()
        self.system_timer.timeout.connect(self.update_system_info)
        self.system_timer.start(2000)  # 每2秒更新一次系统信息
        
    def filter_processes(self, filter_type):
        """过滤进程显示"""
        # 更新按钮状态
        for btn in self.process_filter_buttons:
            btn.setChecked(False)
            
        if filter_type == "all":
            self.show_all_processes_btn.setChecked(True)
        elif filter_type == "user":
            self.show_user_processes_btn.setChecked(True)
        elif filter_type == "system":
            self.show_system_processes_btn.setChecked(True)
        elif filter_type == "services":
            self.show_services_btn.setChecked(True)
            
        self.current_filter = filter_type
        self.update_processes()
        
    def update_processes(self):
        """更新进程列表"""
        # 保存当前选中的进程PID和滚动位置
        selected_pid = None
        current_scroll_value = 0
        
        # 获取选中的进程PID
        selected_rows = self.process_table.selectionModel().selectedRows()
        if selected_rows:
            row = selected_rows[0].row()
            pid_item = self.process_table.item(row, 1)
            if pid_item:
                selected_pid = int(pid_item.text())
                
        # 获取当前滚动位置
        vertical_scrollbar = self.process_table.verticalScrollBar()
        if vertical_scrollbar:
            current_scroll_value = vertical_scrollbar.value()
                
        # 在独立进程中获取进程数据
        try:
            if self.process_pool is None or self.process_pool._state != multiprocessing.pool.RUN:
                self.process_pool = multiprocessing.Pool(processes=1)
            self.process_pool.apply_async(get_processes_data, args=(self.current_filter,), 
                                        callback=lambda processes: self.update_process_table(processes, selected_pid, current_scroll_value))
        except Exception as e:
            print(f"启动进程池时出错: {e}")
            # 如果进程池有问题，直接更新界面
            processes = get_processes_data(self.current_filter)
            self.update_process_table(processes, selected_pid, current_scroll_value)
        
    def update_process_table(self, processes, selected_pid, current_scroll_value):
        """在主线程中更新进程表格"""
        # 确保在主线程中执行界面更新
        if isinstance(processes, dict) and "error" in processes:
            print(f"更新进程时出错: {processes['error']}")
            return
            
        try:
            # 批量更新表格，避免频繁刷新
            self.process_table.setUpdatesEnabled(False)  # 暂停更新以提高性能
            self.process_table.setRowCount(len(processes))
            selected_row = -1
            
            # 创建一个映射来跟踪PID到行的对应关系
            pid_to_row = {}
            
            for row, proc in enumerate(processes):
                # 清除可能存在的合并单元格
                if row == 0:
                    self.process_table.setSpan(row, 0, 1, 1)
                
                # 进程名称
                name = proc['name'] or ''
                item = self.process_table.item(row, 0)
                if item is None:
                    item = QTableWidgetItem(name)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 0, item)
                elif item.text() != name:
                    item.setText(name)
                
                # PID
                pid = proc['pid']
                pid_to_row[pid] = row  # 记录PID到行的映射
                item = self.process_table.item(row, 1)
                if item is None:
                    item = NumericTableWidgetItem(pid)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 1, item)
                elif item.text() != str(pid):
                    item.setText(str(pid))
                
                # 状态
                status = proc['status'] or ''
                item = self.process_table.item(row, 2)
                if item is None:
                    item = QTableWidgetItem(status)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 2, item)
                elif item.text() != status:
                    item.setText(status)
                
                # 用户
                username = proc['username'] or ''
                item = self.process_table.item(row, 3)
                if item is None:
                    item = QTableWidgetItem(username)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 3, item)
                elif item.text() != username:
                    item.setText(username)
                
                # CPU%
                cpu_percent = proc['cpu_percent'] or 0
                cpu_text = f"{cpu_percent:.1f}"
                item = self.process_table.item(row, 4)
                if item is None:
                    item = NumericTableWidgetItem(cpu_text)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    if cpu_percent > 50:
                        item.setForeground(QColor('red'))
                    self.process_table.setItem(row, 4, item)
                else:
                    item.setText(cpu_text)
                    if cpu_percent > 50:
                        item.setForeground(QColor('red'))
                    else:
                        item.setForeground(self.process_table.palette().windowText().color())
                
                # 内存%
                mem_percent = proc['memory_percent'] or 0
                mem_text = f"{mem_percent:.1f}"
                item = self.process_table.item(row, 5)
                if item is None:
                    item = NumericTableWidgetItem(mem_text)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    if mem_percent > 50:
                        item.setForeground(QColor('red'))
                    self.process_table.setItem(row, 5, item)
                else:
                    item.setText(mem_text)
                    if mem_percent > 50:
                        item.setForeground(QColor('red'))
                    else:
                        item.setForeground(self.process_table.palette().windowText().color())
                
                # 内存
                mem_text = ''
                mem_value = 0
                if proc['memory_info']:
                    rss = proc['memory_info'].rss
                    mem_mb = rss / 1024 / 1024
                    mem_text = f"{mem_mb:.1f} MB"
                    mem_value = mem_mb
                item = self.process_table.item(row, 6)
                if item is None:
                    item = NumericTableWidgetItem(mem_text)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 6, item)
                elif item.text() != mem_text:
                    item.setText(mem_text)
                
                # 运行时间
                time_text = ''
                if proc['create_time']:
                    uptime = time.time() - proc['create_time']
                    hours = int(uptime // 3600)
                    minutes = int((uptime % 3600) // 60)
                    seconds = int(uptime % 60)
                    time_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
                item = self.process_table.item(row, 7)
                if item is None:
                    item = QTableWidgetItem(time_text)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 7, item)
                elif item.text() != time_text:
                    item.setText(time_text)
                
                # 命令行
                cmdline = ''
                if proc['cmdline']:
                    cmdline = ' '.join(proc['cmdline'] or [])[:100]  # 限制显示长度
                item = self.process_table.item(row, 8)
                if item is None:
                    item = QTableWidgetItem(cmdline)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 8, item)
                elif item.text() != cmdline:
                    item.setText(cmdline)
                
                # 优先级
                nice = proc['nice'] or 0
                item = self.process_table.item(row, 9)
                if item is None:
                    item = NumericTableWidgetItem(nice)
                    item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)  # 设置左对齐
                    self.process_table.setItem(row, 9, item)
                elif item.text() != str(nice):
                    item.setText(str(nice))
                
                # 检查是否为之前选中的进程
                if selected_pid and proc['pid'] == selected_pid:
                    selected_row = row
                
            # 恢复排序状态
            self.process_table.sortItems(self.sort_column, self.sort_order)
            
            # 如果进行了排序，需要重新查找选中行
            if self.sort_column != 4 or self.sort_order != Qt.DescendingOrder:  # 如果不是默认排序
                # 重新查找选中进程的行号
                if selected_pid and selected_pid in pid_to_row:
                    # 获取排序后的行号
                    for row in range(self.process_table.rowCount()):
                        pid_item = self.process_table.item(row, 1)
                        if pid_item and int(pid_item.text()) == selected_pid:
                            selected_row = row
                            break
        
        except Exception as e:
            print(f"更新进程表格时出错: {e}")
            # 确保在出错时也恢复更新
            self.process_table.setUpdatesEnabled(True)
            
        # 恢复选中状态（在排序后）
        if selected_row >= 0:
            self.process_table.selectRow(selected_row)
            
        self.process_table.setUpdatesEnabled(True)  # 恢复更新
        
        # 恢复滚动位置
        vertical_scrollbar = self.process_table.verticalScrollBar()
        if vertical_scrollbar:
            vertical_scrollbar.setValue(current_scroll_value)
            
    def show_process_context_menu(self, position):
        """显示进程右键菜单"""
        # 获取点击的项目
        item = self.process_table.itemAt(position)
        if item is None:
            return
            
        # 获取选中的行
        selected_rows = self.process_table.selectionModel().selectedRows()
        if not selected_rows:
            return
            
        row = selected_rows[0].row()
        
        # 获取进程信息
        pid_item = self.process_table.item(row, 1)
        name_item = self.process_table.item(row, 0)
        
        if not pid_item or not name_item:
            return
            
        pid = int(pid_item.text())
        name = name_item.text()
        
        # 创建右键菜单
        context_menu = QMenu(self)
        
        # 添加菜单项
        end_task_action = QAction("结束任务", self)
        end_task_action.triggered.connect(lambda: self.end_task_from_context_menu(pid, name))
        context_menu.addAction(end_task_action)
        
        end_task_tree_action = QAction("结束进程树", self)
        end_task_tree_action.triggered.connect(lambda: self.end_process_tree_from_context_menu(pid, name))
        context_menu.addAction(end_task_tree_action)
        
        context_menu.addSeparator()
        
        open_directory_action = QAction("打开进程所在目录", self)
        open_directory_action.triggered.connect(lambda: self.open_process_directory(pid))
        context_menu.addAction(open_directory_action)
        
        properties_action = QAction("属性", self)
        properties_action.triggered.connect(lambda: self.show_process_properties(pid, name))
        context_menu.addAction(properties_action)
        
        # 显示菜单
        context_menu.exec_(self.process_table.viewport().mapToGlobal(position))
        
    def end_task_from_context_menu(self, pid, name):
        """从右键菜单结束任务"""
        # 确认对话框
        reply = QMessageBox.question(
            self, 
            "确认", 
            f"确定要结束进程 {name} (PID: {pid}) 吗？",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            try:
                process = psutil.Process(pid)
                process.terminate()
                QMessageBox.information(self, "成功", f"已发送终止信号给进程 {name}")
                # 等待一段时间后刷新
                QTimer.singleShot(1000, self.refresh_all)
            except psutil.NoSuchProcess:
                QMessageBox.warning(self, "错误", "进程不存在")
            except psutil.AccessDenied:
                QMessageBox.warning(self, "错误", "权限不足，无法终止该进程")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"终止进程时出错: {str(e)}")
                
    def end_process_tree_from_context_menu(self, pid, name):
        """从右键菜单结束进程树"""
        # 确认对话框
        reply = QMessageBox.question(
            self, 
            "确认", 
            f"确定要结束进程树 {name} (PID: {pid}) 吗？这将结束该进程及其所有子进程。",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            try:
                process = psutil.Process(pid)
                # 获取进程树中的所有子进程
                children = process.children(recursive=True)
                # 先终止所有子进程
                for child in children:
                    try:
                        child.terminate()
                    except psutil.NoSuchProcess:
                        pass
                    except psutil.AccessDenied:
                        pass
                
                # 然后终止父进程
                process.terminate()
                
                QMessageBox.information(self, "成功", f"已发送终止信号给进程树 {name}")
                # 等待一段时间后刷新
                QTimer.singleShot(1000, self.refresh_all)
            except psutil.NoSuchProcess:
                QMessageBox.warning(self, "错误", "进程不存在")
            except psutil.AccessDenied:
                QMessageBox.warning(self, "错误", "权限不足，无法终止该进程")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"终止进程树时出错: {str(e)}")
                
    def goto_process(self, pid):
        """转到进程（选中指定PID的进程）"""
        for row in range(self.process_table.rowCount()):
            pid_item = self.process_table.item(row, 1)
            if pid_item and int(pid_item.text()) == pid:
                self.process_table.selectRow(row)
                self.process_table.scrollToItem(pid_item)
                break
                
    def open_process_directory(self, pid):
        """打开进程所在目录"""
        try:
            process = psutil.Process(pid)
            exe_path = process.exe()
            
            if exe_path:
                # 获取目录路径
                directory = os.path.dirname(exe_path)
                if os.path.exists(directory):
                    # 尝试使用不同的文件管理器打开目录
                    file_managers = ['xdg-open', 'nautilus', 'dolphin', 'thunar', 'pcmanfm']
                    opened = False
                    
                    for fm in file_managers:
                        try:
                            subprocess.Popen([fm, directory])
                            opened = True
                            break
                        except FileNotFoundError:
                            continue
                    
                    if not opened:
                        # 如果没有找到文件管理器，尝试使用xdg-open（在大多数Linux发行版中都可用）
                        try:
                            subprocess.Popen(['xdg-open', directory])
                        except FileNotFoundError:
                            QMessageBox.warning(self, "错误", f"无法打开目录：找不到文件管理器\n路径: {directory}")
                else:
                    QMessageBox.warning(self, "错误", f"进程目录不存在\n路径: {directory}")
            else:
                QMessageBox.warning(self, "错误", "无法获取进程可执行文件路径")
                
        except psutil.NoSuchProcess:
            QMessageBox.warning(self, "错误", "进程不存在")
        except psutil.AccessDenied:
            QMessageBox.warning(self, "错误", "权限不足，无法访问该进程信息")
        except Exception as e:
            QMessageBox.warning(self, "错误", f"打开进程目录时出错: {str(e)}")

    def show_process_properties(self, pid, name):
        """显示进程属性"""
        try:
            process = psutil.Process(pid)
            process_info = process.as_dict()
            
            # 创建属性对话框
            dialog = QDialog(self)
            dialog.setWindowTitle(f"进程属性 - {name}")
            dialog.setGeometry(200, 200, 500, 400)
            
            layout = QVBoxLayout(dialog)
            
            # 创建文本区域显示进程信息
            text_edit = QTextEdit()
            text_edit.setReadOnly(True)
            
            # 格式化进程信息
            info_text = f"进程名称: {name}\n"
            info_text += f"进程ID: {pid}\n"
            info_text += f"状态: {process_info.get('status', 'N/A')}\n"
            info_text += f"用户: {process_info.get('username', 'N/A')}\n"
            info_text += f"可执行路径: {process_info.get('exe', 'N/A')}\n"
            info_text += f"当前工作目录: {process_info.get('cwd', 'N/A')}\n"
            info_text += f"启动命令: {' '.join(process_info.get('cmdline', []))}\n"
            info_text += f"父进程ID: {process_info.get('ppid', 'N/A')}\n"
            
            # 内存信息
            memory_info = process_info.get('memory_info', None)
            if memory_info:
                info_text += f"内存使用 (RSS): {memory_info.rss / 1024 / 1024:.2f} MB\n"
                info_text += f"内存使用 (VMS): {memory_info.vms / 1024 / 1024:.2f} MB\n"
            
            # CPU信息
            info_text += f"CPU使用率: {process_info.get('cpu_percent', 0):.2f}%\n"
            info_text += f"CPU时间 (用户): {process_info.get('cpu_times', [0, 0])[0]:.2f} 秒\n"
            info_text += f"CPU时间 (系统): {process_info.get('cpu_times', [0, 0])[1]:.2f} 秒\n"
            
            # 线程信息
            info_text += f"线程数: {process_info.get('num_threads', 'N/A')}\n"
            
            # 打开的文件
            try:
                open_files = process.open_files()
                info_text += f"打开的文件数: {len(open_files)}\n"
                if open_files:
                    info_text += "打开的文件:\n"
                    for f in open_files[:10]:  # 只显示前10个
                        info_text += f"  {f.path}\n"
                    if len(open_files) > 10:
                        info_text += f"  ... 还有 {len(open_files) - 10} 个文件\n"
            except (psutil.AccessDenied, psutil.NoSuchProcess):
                info_text += "打开的文件: 访问被拒绝\n"
            
            # 网络连接
            try:
                connections = process.connections()
                info_text += f"网络连接数: {len(connections)}\n"
                if connections:
                    info_text += "网络连接:\n"
                    for conn in connections[:10]:  # 只显示前10个
                        info_text += f"  {conn.laddr} -> {conn.raddr} ({conn.status})\n"
                    if len(connections) > 10:
                        info_text += f"  ... 还有 {len(connections) - 10} 个连接\n"
            except (psutil.AccessDenied, psutil.NoSuchProcess):
                info_text += "网络连接: 访问被拒绝\n"
            
            text_edit.setText(info_text)
            layout.addWidget(text_edit)
            
            # 添加关闭按钮
            button_layout = QHBoxLayout()
            button_layout.addStretch()
            close_button = QPushButton("关闭")
            close_button.clicked.connect(dialog.close)
            button_layout.addWidget(close_button)
            layout.addLayout(button_layout)
            
            dialog.exec_()
            
        except psutil.NoSuchProcess:
            QMessageBox.warning(self, "错误", "进程不存在")
        except psutil.AccessDenied:
            QMessageBox.warning(self, "错误", "权限不足，无法访问该进程信息")
        except Exception as e:
            QMessageBox.warning(self, "错误", f"获取进程信息时出错: {str(e)}")

    def on_header_clicked(self, column):
        """处理表头点击事件，保存排序状态"""
        self.sort_column = column
        # 切换排序顺序，使箭头方向符合感官习惯
        current_order = self.process_table.horizontalHeader().sortIndicatorOrder()
        self.sort_order = Qt.AscendingOrder if current_order == Qt.DescendingOrder else Qt.DescendingOrder
        
    def update_system_info(self):
        """更新系统信息"""
        try:
            # CPU信息
            cpu_percent = psutil.cpu_percent(interval=0.5)
            self.cpu_label.setText(f"CPU: {cpu_percent:.1f}%")
            
            # 更新CPU图表数据
            self.cpu_data.append(cpu_percent)
            if len(self.cpu_data) > self.max_data_points:
                self.cpu_data.pop(0)
            
            # 更新CPU折线图
            self.cpu_ax.clear()
            self.cpu_ax.plot(self.cpu_data, 'b-', linewidth=1)
            self.cpu_ax.set_title("CPU使用率实时图表")
            self.cpu_ax.set_xlabel("时间点")
            self.cpu_ax.set_ylabel("使用率 (%)")
            self.cpu_ax.set_ylim(0, 100)
            self.cpu_ax.grid(True)
            self.cpu_canvas.draw()
            
            # 内存信息
            memory = psutil.virtual_memory()
            mem_percent = memory.percent
            mem_total = memory.total / 1024 / 1024  # MB
            mem_used = memory.used / 1024 / 1024     # MB
            self.memory_label.setText(f"内存: {mem_percent:.1f}% ({mem_used:.0f} MB / {mem_total:.0f} MB)")
            
            # 更新内存图表数据
            self.memory_data.append(mem_percent)
            if len(self.memory_data) > self.max_data_points:
                self.memory_data.pop(0)
            
            # 更新内存折线图
            self.memory_ax.clear()
            self.memory_ax.plot(self.memory_data, 'g-', linewidth=1)
            self.memory_ax.set_title("内存使用率实时图表")
            self.memory_ax.set_xlabel("时间点")
            self.memory_ax.set_ylabel("使用率 (%)")
            self.memory_ax.set_ylim(0, 100)
            self.memory_ax.grid(True)
            self.memory_canvas.draw()
            
            # 磁盘信息
            disk = psutil.disk_usage('/')
            disk_percent = (disk.used / disk.total) * 100
            disk_total = disk.total / 1024 / 1024  # MB
            disk_used = disk.used / 1024 / 1024    # MB
            self.disk_label.setText(f"磁盘: {disk_percent:.1f}% ({disk_used:.0f} MB / {disk_total:.0f} MB)")
            
            # 更新磁盘图表数据
            self.disk_data.append(disk_percent)
            if len(self.disk_data) > self.max_data_points:
                self.disk_data.pop(0)
            
            # 更新磁盘折线图
            self.disk_ax.clear()
            self.disk_ax.plot(self.disk_data, 'r-', linewidth=1)
            self.disk_ax.set_title("磁盘使用率实时图表")
            self.disk_ax.set_xlabel("时间点")
            self.disk_ax.set_ylabel("使用率 (%)")
            self.disk_ax.set_ylim(0, 100)
            self.disk_ax.grid(True)
            self.disk_canvas.draw()
            
        except Exception as e:
            print(f"更新系统信息时出错: {e}")
            
    def refresh_all(self):
        """刷新所有信息"""
        self.update_processes()
        self.update_system_info()
        
    def end_task(self):
        """结束选中任务"""
        selected_rows = self.process_table.selectionModel().selectedRows()
        if not selected_rows:
            QMessageBox.warning(self, "警告", "请先选择一个进程")
            return
            
        row = selected_rows[0].row()
        pid_item = self.process_table.item(row, 1)
        name_item = self.process_table.item(row, 0)
        
        if not pid_item or not name_item:
            return
            
        pid = int(pid_item.text())
        name = name_item.text()
        
        # 确认对话框
        reply = QMessageBox.question(
            self, 
            "确认", 
            f"确定要结束进程 {name} (PID: {pid}) 吗？",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            try:
                process = psutil.Process(pid)
                process.terminate()
                QMessageBox.information(self, "成功", f"已发送终止信号给进程 {name}")
                # 等待一段时间后刷新
                QTimer.singleShot(1000, self.refresh_all)
            except psutil.NoSuchProcess:
                QMessageBox.warning(self, "错误", "进程不存在")
            except psutil.AccessDenied:
                QMessageBox.warning(self, "错误", "权限不足，无法终止该进程")
            except Exception as e:
                QMessageBox.warning(self, "错误", f"终止进程时出错: {str(e)}")
                
    def new_task(self):
        """启动新任务"""
        task, ok = QInputDialog.getText(self, "新任务", "请输入要启动的程序名称或命令:")
        if ok and task:
            try:
                process = QProcess(self)
                process.startDetached(task)
                QMessageBox.information(self, "成功", f"已启动任务: {task}")
                QTimer.singleShot(1000, self.refresh_all)
            except Exception as e:
                QMessageBox.warning(self, "错误", f"启动任务时出错: {str(e)}")


def main():
    # 首先创建QApplication实例
    app = QApplication(sys.argv)
    
    # 设置应用程序名称和组织信息，有助于桌面环境正确显示图标
    app.setApplicationName("任务管理器")
    app.setApplicationDisplayName("Linux任务管理器")
    app.setOrganizationName("Linux")
    app.setOrganizationDomain("linux.org")
    
    # 尝试使用小图标文件
    small_icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon_small.png")
    if not os.path.exists(small_icon_path):
        # 如果小图标不存在，尝试使用原始图标
        small_icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
    
    print(f"尝试加载图标: {small_icon_path}")
    
    # 设置应用程序图标
    if os.path.exists(small_icon_path):
        # 现在QApplication已创建，可以安全使用QPixmap
        try:
            # 直接使用QIcon
            app_icon = QIcon(small_icon_path)
            app.setWindowIcon(app_icon)
            print("已设置应用程序图标")
            
            # 使用QPixmap创建图标
            try:
                pixmap = QPixmap(small_icon_path)
                if not pixmap.isNull():
                    app.setWindowIcon(QIcon(pixmap))
                    print("通过QPixmap设置图标成功")
            except Exception as e:
                print(f"使用QPixmap设置图标时出错: {e}")
        except Exception as e:
            print(f"设置图标时出错: {e}")
    else:
        print(f"图标文件不存在: {small_icon_path}")
    
    # 创建并显示任务管理器窗口
    task_manager = LinuxTaskManager()
    
    # 确保窗口也设置了图标
    if 'app_icon' in locals() and not app_icon.isNull():
        task_manager.setWindowIcon(app_icon)
        print("已为窗口设置图标")
    
    # 显示窗口并运行应用程序
    task_manager.show()
    sys.exit(app.exec_())


def verify_icon_file(icon_path):
    """验证图标文件并返回可用的图标对象"""
    if not os.path.exists(icon_path):
        print(f"图标文件不存在: {icon_path}")
        return None
        
    # 检查文件权限
    try:
        with open(icon_path, 'rb') as f:
            # 读取前几个字节检查是否为有效的PNG文件
            header = f.read(8)
            is_png = header.startswith(b'\x89PNG\r\n\x1a\n')
            if not is_png:
                print(f"警告: 文件不是有效的PNG格式: {icon_path}")
    except Exception as e:
        print(f"读取图标文件时出错: {e}")
        return None
    
    # 只使用QIcon加载图标，避免在QApplication创建前使用QPixmap
    try:
        # 直接使用QIcon
        icon = QIcon(icon_path)
        if not icon.isNull():
            print("成功加载图标")
            return icon
            
        # 尝试使用绝对路径
        abs_path = os.path.abspath(icon_path)
        icon = QIcon(abs_path)
        if not icon.isNull():
            print(f"使用绝对路径成功加载图标: {abs_path}")
            return icon
            
        print("图标加载失败")
        return None
    except Exception as e:
        print(f"加载图标时出错: {e}")
        return None

if __name__ == "__main__":
    multiprocessing.set_start_method('spawn')  # 在Linux上使用spawn方式启动进程
    
    # 检查图标文件是否存在，但不加载它
    icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
    if os.path.exists(icon_path):
        print(f"图标文件存在: {icon_path}")
    else:
        print(f"图标文件不存在: {icon_path}")
        
    # 直接启动应用程序，在QApplication创建后再加载图标
    main()